package ro.flashbite.game_engine.view.sheet.cpu
{
	import com.greensock.TweenMax;
	import com.greensock.easing.Linear;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.PixelSnapping;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import ro.flashbite.errors.ArgumentIncompatibleError;
	import ro.flashbite.game_engine.view.sheet.BaseSpriteSheet;
	import ro.flashbite.helpers.MathHelpers;
	
	/**
	 * Base class that can act like a MovieClip, but is uses a SpriteSheet bitmapData and a vector of Rectangles to draw visuals with extra speed.
	 * Can only play frame bases animation; No FrameLabels are recognized
	 *  
	 * v 1.0
	 * 
	 * @author Adrian Barbu
	 */	
	public class SimpleSpriteSheetCPU extends BaseSpriteSheet
	{
		private static const DEFAULT_POINT:Point = new Point(); //used for performance in blit function
		
		private static const DEFAULT_SPEED:Number = 1 / 30; //30 frames per second
		
		private var _animation:TweenMax;
		
		protected var _currentFrame:uint;
		protected var _ccurentFrame:Number; //used on render
		protected var _totalFrames:uint;
		
		protected var _visual:Bitmap; //current bmp visible on this
		protected var _visualBmpData:BitmapData; //bmpData of the current bmp
		
		protected var _currentBlittedBmpData:BitmapData; //the bmpData from were the slices are copied to _visualBmpData
		protected var _currentBlittedRectangle:Rectangle; //the rect used in slicing the _currentBlittedBmpData
		
		protected var _rectangles:Vector.<Rectangle>; //holds all rectangles used to slice _currentBlittedBmpData
		
		// ================================================================================================================================
		// CONSTRUCTOR
		// ================================================================================================================================
		
		public function SimpleSpriteSheetCPU(sheetBmpData:BitmapData, settings:XML)
		{
			_constructor(sheetBmpData, settings);
		}
		private function _constructor(sheetBmpData:BitmapData, settings:XML):void
		{
			if (sheetBmpData == null && settings == null) return; //called from children; no code will be executed here
			
			_currentBlittedBmpData = sheetBmpData;
			
			//save blitting rectangles
			_rectangles = new Vector.<Rectangle>();
			var xmlListSlices:XMLList = settings.slices.slice as XMLList,
				xmlSlice:XML,
				rect:Rectangle;
			for each (xmlSlice in xmlListSlices) {
				rect = new Rectangle(Number(xmlSlice.@x), Number(xmlSlice.@y), Number(xmlSlice.@width), Number(xmlSlice.@height));
				_rectangles.push(rect);
			}
			
			//set total frames
			_totalFrames = _rectangles.length;
			
			//create globals
			_visualBmpData = new BitmapData(rect.width, rect.height, true, 0x00FFFFFF);
			_visual = new Bitmap(_visualBmpData, PixelSnapping.NEVER, true);
			
			//add bmp to this
			this.addChild(_visual);
			
			//stop it on frame 1
			gotoAndStop(1);
			//disable interaction
			disable();
		}
		
		// ================================================================================================================================
		// ISleepWakeupDestroy
		// ================================================================================================================================
		
		override public final function sleep():void
		{
			if (_animation) _animation.pause();
		}
		
		override public final function wakeup():void
		{
			if (_animation) _animation.resume();
		}
		
		override public function destroy():void
		{
			if (_animation) {
				_animation.kill();
				_animation = null;
			}
			
			if (_visual) {
				if (_visual.parent) _visual.parent.removeChild(_visual);
				_visual = null;
			}
			_visualBmpData = null;
			_currentBlittedBmpData = null;
			_currentBlittedRectangle = null;
			_rectangles = null;
		}
		
		// ================================================================================================================================
		// IInteractive
		// ================================================================================================================================
		
		override public final function enable()	: void 	{ this.mouseEnabled = this.mouseChildren = true; 	}
		override public final function disable(): void 	{ this.mouseEnabled = this.mouseChildren = false; 	}
		
		// ================================================================================================================================
		// MovieClip like behaviour
		// ================================================================================================================================
		
		/**
		 * Basic play
		 * 
		 * @param from : start frame
		 * @param to : end frame
		 * @param speed : speed of animation -> 1 is normal, 2 is fast , .5 is slow, etc...
		 * @param repeatCount : num on repeats
		 * @param onCompleteCall : function to call on complete
		 * @return (Number) duration in seconds of the anim
		 */		
		override public final function playFromTo(from:Object, to:Object, speed:Number = 1, repeatCount:int = 0, onCompleteFunc:Function = null, onCompleteParams:Array = null):Number
		{
			if (from is uint && to is uint) {
				const fromm:uint = from as uint,
					  too:uint = to as uint;
				if ( MathHelpers.isBetween(fromm, 1, _totalFrames) && MathHelpers.isBetween(too, 1, _totalFrames) ) {
					//stop on from frame
					gotoAndStop(fromm);
					//calculate time
					var difFrames:uint;
					if (fromm >= too) {
						difFrames = fromm - too;
					} else difFrames = too - fromm;
					//calculate time
					const time:Number = difFrames * (DEFAULT_SPEED / speed);
					//start animation
					_animation = TweenMax.to(
												this, 
												time, 
												{ 
													__ccurentFrame:too, //use number values that will be rounded
													ease:Linear.easeNone, 
													onComplete:onCompleteFunc, 
													onCompleteParams:onCompleteParams,
													repeat:repeatCount 
												}
											);
					//return time
					return time;
				} else throw new ArgumentIncompatibleError("from ||&& to");
			} else throw new ArgumentIncompatibleError("from ||&& to");
			//return error
			return -1;
		}
		
		override public final function gotoAndStop(frame:Object):void
		{
			if (frame is uint) {
				currentFrame = frame as uint;
				_ccurentFrame = _currentFrame;
				stop();
			} else {
				throw new ArgumentIncompatibleError("frame");
			}
		}
		
		override public final function stop():void
		{
			if (_animation) {
				_animation.kill();
			}
		}
		
		// ================================================================================================================================
		// PUBLIC
		// ================================================================================================================================
		
		/**
		 * Copy the bmpData from specific frame on another bitmapData at some position
		 *  
		 * @param frame : (uint) frame to copy bitmapData
		 * @param bmpData : (BitmapData) instance of bitmapData were pixels from frame will be copied
		 * @param point : (Point) location in given bitmapData were pixels will be copied
		 */		
		override public final function copyPixelsFromFrame(frame:uint, bmpData:BitmapData, point:Point):void
		{
			if (_currentFrame == frame) {
				bmpData.copyPixels(_currentBlittedBmpData, _currentBlittedRectangle, point);
			} else {
				var initFrame:uint = _currentFrame;
				currentFrame = frame;
				bmpData.copyPixels(_currentBlittedBmpData, _currentBlittedRectangle, point);
				currentFrame = initFrame;
			}
		}
		
		// ================================================================================================================================
		// PROTECTED -> to be overriden by children
		// ================================================================================================================================
		
		protected function createBlittValues():void
		{
			//_currentFrame is 1 based, _rectangles are 0 based
			_currentBlittedRectangle = _rectangles[_currentFrame - 1];
		}
		
		// ================================================================================================================================
		// PRIVATE
		// ================================================================================================================================
		
		private function blitt():void
		{
			_visualBmpData.copyPixels(_currentBlittedBmpData, _currentBlittedRectangle, DEFAULT_POINT);
		}
		
		// ================================================================================================================================
		// GETTERS, SETTERS
		// ================================================================================================================================
		
		override public final function get isPlaying():Boolean { return ( _animation && (_animation.active) && (_animation.paused == false)); }
		
		override public final function get __ccurentFrame()		   : Number { return _ccurentFrame; }
		override public final function set __ccurentFrame(v:Number): void 
		{ 
			_ccurentFrame = v; 
			//_ccurentFrame = Math.round(_ccurentFrame);
			//faster version of Math.round -> on mobile (Android) 66.6% faster; on mobile (iPhone/iPad) 18.6% faster; on PC : 57.71% faster 
			_ccurentFrame = (_ccurentFrame + 0.5) >> 0;
			
			if (_ccurentFrame < 1) _ccurentFrame = 1;
			else if (_ccurentFrame > _totalFrames) _ccurentFrame = _totalFrames;
				
			currentFrame = _ccurentFrame;	
		}
		
		override public final function get currentFrame():uint { return _currentFrame; }
		override public final function set currentFrame(v:uint):void
		{
			//if (_currentFrame == value) return; //no performance saving in this check
			
			//save currentFrame 
			_currentFrame = v;
			//trace("Current frame : " + _currentFrame);
			
			//create blit values
			createBlittValues();
			
			//draw the frame
			blitt();
		}
		
		override public final function get totalFrames():uint { return _totalFrames; }

		override public final function get animation():TweenMax { return _animation; }
	}
}